{{*/*
  * Ajax
  * Wrapper object for cross-browser Ajax support.
  */*}}

{{*/*
  * Constructor.
  * @param callBackFunc (Function) Function to call when loading is complete
  * @param options (Property Object) Various options: context (Object) The context of the caller, params (Object) Any parameters to pass through, method (String) HTTP method either GET or POST
  */*}}
function Ajax(callBackFunc, options) {
  this._xmlhttp = null;
  this._completeFunc = callBackFunc;

  this._context = (options && options.context != null) ? options.context : null;
  this._completeParams = (options && options.params != null) ? options.params : null;
  this._timeOut = (options && options.timeout) ? options.timeout : null;

  this._paramsIsObject = (this._completeParams && (typeof(this._completeParams) == 'object'));

  this._errTimeout = -1;
  this._errLoadFailed = -2;

  var objs = [ function() { return new XMLHttpRequest(); },
               function() { return new ActiveXObject('Msxml2.XMLHTTP'); },
               function() { return new ActiveXObject('Microsoft.XMLHTTP'); } ];

  for (var i = 0; i < objs.length; i++) {
    var ob = objs[i];
    try {
      this._xmlhttp = ob();
      break;
    } catch (e) {
    }
  }
};

{{*/*
  * Make an Ajax request for data.
  * @param uri The uri to retrieve data from
  * @param data The data to send, should be null for GET requests (the default) and a query string for POST
  */*}}
Ajax.prototype._request = function(uri, data) {
  if (!this._xmlhttp) {
    return;
  }

  if (this._timeId) {
    clearTimeout(this._timeId);
    this._timeId = null;
  }

  if (this._isBusy) {
    this._xmlhttp.onreadystatechange = function() {};
    this._xmlhttp.abort();
  }

  var _self = this;
  this._xmlhttp.open(((data == null) ? 'GET' : 'POST'), uri, true);

  if (this._timeOut) {
    this._timeId = setTimeout(function() { _self._onTimeout.call(_self); }, this._timeOut);
  }

  this._isBusy = true;

  if (data != null) {
    this._xmlhttp.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
  }
  this._xmlhttp.onreadystatechange = function() { _self._onReadyStateChange.call(_self); };
  this._xmlhttp.send(data);
};

{{*/*
  * Calls the complete function.
  * @param xml The resulting xml
  * @param code The resulting status code
  */*}}
Ajax.prototype._callComplete = function(xml, code) {
  if (this._paramsIsObject) {
    this._completeParams._status = code;
  }

  if (this._context) {
    this._completeFunc.call(this._context, xml, this._completeParams);
  } else {
    this._completeFunc(xml, this._completeParams);
  }
};

{{*/*
  * Handler when the ready state changes.
  */*}}
Ajax.prototype._onReadyStateChange = function() {
  {{* /* States: 0 - Uninitialized, 1 - Loading, 2 - Loaded, 3 - Interactive, 4 - Complete */ *}}
  if (this._xmlhttp.readyState != 4) {
    return;
  }

  var data, code;

  if (this._timeId) {
    clearTimeout(this._timeId);
    this._timeId = null;
  }

  try {
    data = (this._xmlhttp.responseXML) ? this._xmlhttp.responseXML : null;
    code = (this._xmlhttp.status) ? this._xmlhttp.status : this._errLoadFailed;

  } catch (e) {
    data = null;
    code = this._errLoadFailed;
  }

  this._xmlhttp.onreadystatechange = function() {};
  this._isBusy = false;

  this._callComplete(data, code);
};

{{*/*
  * Handler when the request times out.
  */*}}
Ajax.prototype._onTimeout = function() {
  this._xmlhttp.onreadystatechange = function() {};
  this._xmlhttp.abort();

  this._isBusy = false;
  this._timeId = null;

  this._callComplete(null, this._errTimeout);
};

